#include "ResourceCache.h"

//////////////////////////////////////////
/////TARGET CODE TO GET A RESOURCE
/////OLD ONE
//////////////////////////////////////////
//
//	ResCache ResourceCache;
//	
//	//////SLOW///////
//	ResourceFilePtr MainMenuResources(	RE_NEW ZipResourceFile("MainMenuAssets.zip")	);
//	ResourceCache.AddResFile(	MainMenuResources	);
//	ResHandlePtr background = ResourceCache.GetHandle(	Resource("background.png")	);
//
//	//////Faster//////
//	ResourceFilePtr MainMenuResources(	RE_NEW ZipResourceFile("MainMenuAssets.zip")	);
//	int id = ResourceCache.AddResFile(	MainMenuResources	);
//	ResHandlePtr background = ResourceCache.GetHandle(	Resource("background.png"), id	);
//////////////////////////////////////////
/////Target Design(New)
//////////////////////////////////////////
/*
FilePath file("MainMenuAssets.zip/background.png"); // the actuall path to the file to be loaded


//*/

ResCache::ResCache(unsigned int Bytes_Cap)
{
	m_LastResourceFileId = 0;
	m_BytesUsed = 0;
	VSetBytesCap(Bytes_Cap);
}
ResCache::~ResCache()
{
	m_LeastRecentUsed.clear();
	m_Resources.clear();

}
ResHandle::~ResHandle() {
	if(m_buffer != NULL)
	{
		delete [] m_buffer;
	}
	if(m_Cache != NULL)
	{
		m_Cache->VBytesReleased(m_size);
	}
}


//////////////////////////////////////////////////////
////////Res Cache Implementation
unsigned int ResCache::VAddResourceFile(RE::IO::FilePath & path, ResourceFilePtr ResFile)
{
	//check if the res file is not already here
	ResourceFileMap::iterator i = m_ResourceFileMap.find(path);
	if(i != m_ResourceFileMap.end())
	{
		return 0;
	}
	
	m_ResourceFileMap.insert(std::make_pair( path, ResFile ));
	
	return 1;
}

bool ResCache::VRemoveResourceFile(RE::IO::FilePath & path, bool RemoveHandles) //NOTE: QUITE EXPENSIVE OPERATION IF REMOVEH IS TRUE
{
	ResourceFileMap::iterator it = m_ResourceFileMap.find(path);
	if(it != m_ResourceFileMap.end())
	{
		m_ResourceFileMap.erase(it);
	}else
	{
		return false;
	}
	if(RemoveHandles)
	{
		//Loop Through LeastResent Handles and remove them
		ResHandleList::iterator begin = m_LeastRecentUsed.begin();
		ResHandleList::iterator end = m_LeastRecentUsed.end();
		while(begin != end)
		{
			ResHandleMap::iterator reshm_i = m_Resources.begin();
			while(reshm_i != m_Resources.end())
			{
				if((*reshm_i).first == ResFile_Id) // is this member of the file we are clearing
				{
					if(&*(*reshm_i).second.second.get() == &*(*begin).get()) // compare addresses of the handles
					{
						m_LeastRecentUsed.remove((*begin)); // if equal -> we have to remove it
					}
				}
				reshm_i++;
			}
			begin++;
		}
		//Erase everything with this id;
		m_Resources.erase( ResFile_Id );
	}
	return true;
}







ResHandlePtr ResCache::VGetHandle(const RE::IO::FilePath &res)
{
	std::string fileid = res.GetFileName();
	if(fileid == 0) // no id speicified. Valid values start from 1
	{
		fileid = VGetResFileIdByFileName(res.GetName());
		if(fileid == 0)
		{
			assert("Unknown Error in ResCache::LoadHandle()");
		}
	}
	ResHandlePtr handle = FindHandle(res);
	if(handle == ResHandlePtr())
	{
		return LoadHandle(res);
	}else
	{
		UpdateHandle(handle);
		return handle;
	}
}

ResHandlePtr ResCache::FindHandle( const Resource &res)
{
	ResHandleMap::iterator file_iter = m_Resources.begin();
	while(file_iter != m_Resources.end())
	{
		if(file_iter->first == res.GetId())
		{
			if(file_iter->second.first == res.GetName())
			{
				return file_iter->second.second;
			}
		}
		file_iter++;
	}
	return ResHandlePtr();
}


ResHandlePtr ResCache::LoadHandle(const Resource &res)
{
	IResourceFile * file = m_ResourceFileMap.find(res.GetId())->second.get();
	ResHandlePtr r(RE_NEW ResHandle(*this, res));
	ResHandle * handle = dynamic_cast<ResHandle*>(r.get());
	if(file->VFileExist(res.GetName()))
	{
		handle->m_size = file->VGetResourceSize(res);
		//Now Make Sure There is enough memory in the cache
		if(r->VGetSize() < VGetBytesCap())
		{
			//now check if we will need to free momory and calculate how much is it
			unsigned int free_memory = VGetBytesCap() - VGetUsedBytes();
			if(r->VGetSize() > free_memory)
			{
				VFreeMemory( handle->m_size ); // note: its not mistake!! This function take HOW MUCH MEMORY SHOUD BE FREE!!!
			}
		}else
		{
			assert(0 && "The requested file is too large for this cache.");
			return ResHandlePtr();
		}
		handle->m_buffer = RE_NEW char[r->VGetSize()];
		handle->m_Cache->VBytesAllocated(r->VGetSize());
		file->VGetResource(res, handle->m_buffer);
	}else
	{
		assert(0 && "Attempt to load unknown resource!");
		return ResHandlePtr();
	}
	m_LeastRecentUsed.push_front(r);
	m_Resources.insert(std::pair<unsigned int, std::pair<std::string, ResHandlePtr>>(res.GetId(), std::pair<std::string, ResHandlePtr>( std::string(res.GetName()), r)));
	return r;
}
unsigned int ResCache::VGetResFileIdByFileName(const std::string & str)
{
	ResourceFileMap::iterator i = m_ResourceFileMap.begin();
	ResourceFileMap::iterator end = m_ResourceFileMap.end();
	while(i != end)
	{
		if((*i).second->VGetFilename() == str)
		{
			return (*i).first;
		}
		++i;
	}
	return 0;
}
unsigned int ResCache::GetResFileIdByFileName(Resource & res)
{
	//loop through all resource files and exit when the res is found
	ResourceFileMap::iterator res_begin = m_ResourceFileMap.begin();
	while(res_begin != m_ResourceFileMap.end())
	{
		if((*res_begin).second->VFileExist(res.GetName().c_str()))
		{
			return (*res_begin).first;
			break;
		}
		res_begin++;
	}
	return 0;
}

void ResCache::UpdateHandle(ResHandlePtr handle)
{
	m_LeastRecentUsed.remove(handle);
	m_LeastRecentUsed.push_front(handle);
}



bool ResCache::FreeLeastRecentUsedHandle()
{
	if(m_LeastRecentUsed.size() > 0)
	{
		ResHandleList::iterator begin = m_LeastRecentUsed.end();
		begin--;
		ResHandlePtr handle(*begin);
		m_LeastRecentUsed.pop_back();
		ResHandleMap::iterator b_iter = m_Resources.begin();
		while(b_iter != m_Resources.end())
		{
			if(b_iter->first == handle->VGetResource().GetId())
			{
				if(b_iter->second.second.get() == handle.get())
				{
					m_Resources.erase(b_iter);
					return true;
				}
			}
			b_iter++;
		}
	}
	return false;
}

void ResCache::VFreeMemory(unsigned int bytes)
{
	unsigned int FreeBytes = 0;
	while(FreeBytes < bytes)
	{
		///// check if we can free something
		if( (int)m_LeastRecentUsed.size() == 0)
		{
			assert(0 && "There are no resources to free. This is to avoid infinite loop.");
			return;
		}

		FreeLeastRecentUsedHandle();
		FreeBytes = VGetBytesCap() - VGetUsedBytes();
	}
}




